package Model;

import java.util.EnumSet;
import java.util.Stack;

import javax.swing.plaf.SliderUI;

import Common.Direction;
import Common.DirectionMethods;

public class Maze {
	
	private int m_Width = 0; 
	private int m_Height = 0; 
	private Cell[][] m_Cells = new Cell[0][0];
	
	//Constructor
	public Maze(int width, int height)
	{
		m_Width = width;
		m_Height = height;
		m_Cells = new Cell[m_Width][m_Height];
		for(int i=0; i < width;i++)
		{
			for(int j=0; j < height;j++)
			{
			  	m_Cells[i][j] = new Cell(i, j);
			} 	
		}
	}
	
	public int GetWidth()
	{
		return m_Width;
	}
	
	public int GetHeigth()
	{
		return m_Height;
	}
	
	public void GenerateMaze()
	{
		
		//Create Cell selection stack
		Stack<Cell> CellStack = new Stack<Cell>();
		CellStack.ensureCapacity(m_Height * m_Width);
		
		//Random start point. 
		do
		{
		    int x = (int)(Math.random() * m_Width);
			int y = (int)(Math.random() * m_Height);
			if(x >= 0 
			  && x < m_Cells.length
			  && y >= 0
			  && y < m_Cells[x].length)
			{
				CellStack.push(m_Cells[x][y]);
			}
		}
		while (CellStack.empty());
		
		
		while (!CellStack.empty())
		{
			Cell cell = CellStack.pop();
			while(true)
			{
				//FireOnVisitedCellEvent(cell)
				if(!cell.VisidtedWhileGeneration)
				{
					cell.VisidtedWhileGeneration = true;
				}
				
				EnumSet<Direction> possibleDirection = DirectionMethods.GetPossibleDirectionsForSolutionPath(m_Cells, cell);
				
				int possibleDirectionCount = DirectionMethods.CountDirections(possibleDirection);
				if(possibleDirectionCount == 0)
				{
					// no more options for this cell
					break;
				}
				
				EnumSet<Direction> wallToRemove = EnumSet.noneOf(Direction.class);
				if(possibleDirectionCount == 1)
				{
					// Go in last possible direction
					wallToRemove = possibleDirection.clone();
				}
				else
				{
					// More than one direction left
					// put in on stack we'll use that later
					CellStack.push(cell);
					
					int randDirectionIndex = (int)(Math.random() * possibleDirectionCount);
				
					wallToRemove.clear();
					//Fail, got random index of possibles directions. 
					// and false direction
					int i = 0;
					for (Direction direction : Direction.values()) 
					{
						if(possibleDirection.contains(direction))
						{
							if(i == randDirectionIndex)
							{
								wallToRemove.add(direction);
								break;
							}
							i++;
						}
					}
				}
				
				cell.RemoveWall(wallToRemove);
				//FireOnVisitEvent(cell)
				
				//Visit the cell in the new direction 
				// and remove the wall between the two cells
				
				if(wallToRemove.contains(Direction.TOP))
				{
					cell = m_Cells[cell.X][cell.Y - 1];
					cell.RemoveWall(Direction.BOTTOM);
				}
				if(wallToRemove.contains(Direction.RIGHT))
				{
					cell = m_Cells[cell.X + 1][cell.Y];
					cell.RemoveWall(Direction.LEFT);	
				}
				if(wallToRemove.contains(Direction.BOTTOM))
				{
					cell = m_Cells[cell.X][cell.Y + 1];
					cell.RemoveWall(Direction.TOP);		
				}
				if(wallToRemove.contains(Direction.LEFT))
				{
					cell = m_Cells[cell.X - 1][cell.Y];
					cell.RemoveWall(Direction.RIGHT);
				}
				//FireOnVisitEvent(cell)
				PrintMaze();
				try {
					//maze speed sleep
					Thread.sleep(25);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					
				}
			}
		}
		//EmmitEndpoints
		
		
		
			//CellStack.c
	}
	
	public void PrintMaze()
	{
		StringBuilder sb = new StringBuilder();
		for(int h=0; h < GetHeigth(); h++)
		{
			for(int w=0; w < GetWidth(); w++)
			{
				sb.append('+');
				if(m_Cells[w][h].Walls.contains(Direction.TOP))
				{
					sb.append('-');
				}
				else
				{
					sb.append(' ');
				}
			}
			sb.append('+');
			sb.append('\n');
			
			for(int w=0; w < GetWidth(); w++)
			{				
				if(m_Cells[w][h].Walls.contains(Direction.LEFT))
				{
					sb.append('|');
				}
				else
				{
					sb.append(' ');
				}
				sb.append(' ');
			}
			if(m_Cells[GetWidth()-1][h].Walls.contains(Direction.RIGHT))
			{
				sb.append('|');
			}
			else
			{
				sb.append(' ');
			}
			sb.append('\n');
		}
		for(int w=0; w < GetWidth(); w++)
		{
			sb.append('+');
			if(m_Cells[w][GetHeigth()-1].Walls.contains(Direction.BOTTOM))
			{
				sb.append('-');
			}
			else
			{
				sb.append(' ');
			}
		}
		sb.append('+');
		sb.append('\n');
		
		System.out.println(sb.toString());
	}
}

